package oauth

import (
	"bytes"
	"net/http"
	"path/filepath"
	"strings"

	"chainmaker.org/chainmaker/smarteditor/global"

	"chainmaker.org/chainmaker/smarteditor/conf"
	"chainmaker.org/chainmaker/smarteditor/oauth/chainmaker"
	"chainmaker.org/chainmaker/smarteditor/session"
	"github.com/hokaccha/go-prettyjson"
)

var _ AuthServerHandler = (*ChainMakerAuthServerHandler)(nil)

type ChainMakerAuthServerHandler struct {
}

func (cm ChainMakerAuthServerHandler) GetAuthServerFlag() string {
	return string(ChainMaker)
}

func (cm ChainMakerAuthServerHandler) LoginRedirectHandler(w http.ResponseWriter, r *http.Request, stateCode string, site string) {

	oauthUrl := cm.buildOauthUrl(
		conf.Wide.ChainMakerOAuthServer,
		conf.Wide.ChainMakerAuthUrl,
		conf.Wide.ChainMakerClientId,
		conf.Wide.ChainMakerClientSecret,
		conf.Wide.PublicServer,
		stateCode,
		site,
	)

	http.Redirect(w, r, oauthUrl, http.StatusSeeOther)
}

func (cm ChainMakerAuthServerHandler) LoginCallbackHandler_old(w http.ResponseWriter, r *http.Request, stateCode string, site string) {
	authCode := r.URL.Query().Get("code")

	//oidc info
	redirectUrl := GetRedirectUrl(conf.Wide.PublicServer, cm.GetAuthServerFlag(), stateCode, site)
	oidc, err := chainmaker.Oidc(authCode, redirectUrl)
	if err != nil {
		log.Errorf("[ChainMakerAuthServerHandler] [LoginCallbackHandler] get oidc info failed. err: %v", err)
		http.Error(w, "get oidc info failed", http.StatusBadRequest)
		return
	}
	log.Debugf("[ChainMakerAuthServerHandler] [LoginCallbackHandler] oidc:%v", oidc)

	//get user info
	baseInfoUrl := conf.Wide.ChainMakerOAuthServer + conf.Wide.ChainMakerBaseUrl + conf.Wide.ChainMakerUserDetailUrl + oidc.PreferredUsername
	userInfo, err := chainmaker.GetRemoteUserInfo(baseInfoUrl, oidc.AccessToken)
	if err != nil {
		log.Errorf("[ChainMakerAuthServerHandler] [LoginCallbackHandler] get user info failed. err: %v", err)
		http.Error(w, "get user info failed", http.StatusBadRequest)
		return
	}
	j, _ := prettyjson.Marshal(userInfo)
	log.Debugf("[ChainMakerAuthServerHandler] [GetRemoteUserInfo] userInfo:%s", j)

	userName := userInfo.Data.Phone
	if len(userName) == 0 {
		userName = oidc.PreferredUsername
	}
	//handle login
	id := session.LoginCallbackHandler(userInfo.Data.Id, userName, "", site, w, r)
	global.UserTokenCache.Store(id, oidc)
}

// http://192.168.2.90:7080/login/callback?userid=12345
func (cm ChainMakerAuthServerHandler) LoginCallbackHandler(w http.ResponseWriter, r *http.Request, stateCode string, site string) {

	uid := r.URL.Query().Get("userid")
	site = conf.Wide.FrontUrl

	user := conf.GetUser(uid)

	if nil == user {
		cm.addUser(uid, uid, "")
	}

	userInfo := chainmaker.RespUserInfo{
		Code:    200,
		Message: "OK",
		Success: true,
		Data: chainmaker.UserInfoData{
			// Id:    "12345",
			Id:        uid,
			Phone:     "",
			Email:     "",
			IdentList: nil,
		},
	}
	j, _ := prettyjson.Marshal(userInfo)
	log.Debugf("[ChainMakerAuthServerHandler] [GetRemoteUserInfo] userInfo:%s", j)

	userName := uid
	if len(userName) == 0 {
		userName = "PreferredUsername"
	}
	//handle login
	id := session.LoginCallbackHandler(userInfo.Data.Id, userName, "", site, w, r)
	global.UserTokenCache.Store(id, nil)
}

func (cm ChainMakerAuthServerHandler) ThirdPartLoginHandler(w http.ResponseWriter, r *http.Request, site string) {
	userName := r.URL.Query().Get("userName")
	token := r.URL.Query().Get("token")
	baseInfoUrl := conf.Wide.ChainMakerOAuthServer + conf.Wide.ChainMakerBaseUrl + conf.Wide.ChainMakerUserDetailUrl + userName

	userInfo, err := chainmaker.GetRemoteUserInfo(baseInfoUrl, token)
	if err != nil {
		log.Errorf("[ChainMakerAuthServerHandler] [ThirdPartLoginHandler] get user info failed. err: %v", err)
		//http.Error(w, "get user info failed", http.StatusBadRequest)
		http.Redirect(w, r, site, http.StatusFound)
		return
	}

	//handle login
	session.LoginCallbackHandler(userInfo.Data.Id, userName, "", site, w, r)
}

func (cm ChainMakerAuthServerHandler) buildOauthUrl(oauthServer, oauthUrl, clientId, clientSecret, publicServer, stateCode, site string) string {
	buffer := bytes.Buffer{}

	//auth server
	buffer.WriteString(oauthServer + oauthUrl + "?")
	buffer.WriteString("client_id=")
	buffer.WriteString(clientId)
	buffer.WriteString("&response_type=code")
	buffer.WriteString("&state=")
	buffer.WriteString(clientSecret)
	//redirect url
	buffer.WriteString("&redirect_uri=")
	buffer.WriteString(GetRedirectUrl(publicServer, cm.GetAuthServerFlag(), stateCode, site))

	return buffer.String()
}

func (cm ChainMakerAuthServerHandler) addUser(userID, userName, userAvatar string) string {
	// addUserMutex.Lock()
	// defer addUserMutex.Unlock()
	userID = strings.ToLower(userID)
	user := conf.GetUser((userID))
	if user != nil {
		return "user exists"
	}
	///Users/boom/wide/workspaces/109
	workspace := filepath.Join(conf.Wide.Data, "workspaces", userID)
	newUser := conf.NewUser(userID, userName, userAvatar, workspace)
	//	conf.Users = append(conf.Users, newUser)
	if !newUser.Insert() {
		return "user create error"
	}

	if !conf.CreateWorkspaceDir(workspace) {
		cm.initContractExample(workspace)
	}
	//conf.UpdateCustomizedConf(userID)

	log.Infof("Created a user [%s]", userID)

	return "user created"
}

func (cm ChainMakerAuthServerHandler) initContractExample(workspace string) {
	dir := workspace + conf.PathSeparator + "src"
	err := conf.CpDockGoContractExToWS(dir)
	if err != nil {
		log.Error(err)
		return
	}
	return
}
